from typing import Any, Optional
import httpx
import sys
import json
import configparser
import os
from httpx import TimeoutException, HTTPError
from mcp.server.fastmcp import FastMCP  # MCP SDK核心类


# -------------------------- 1. 初始化MCP服务器实例 --------------------------
# 服务器名称设为"weather-amap"，便于客户端识别
mcp = FastMCP("weather-amap")


# -------------------------- 2. 配置读取（环境变量优先，兼容配置文件） --------------------------
def get_amap_api_key(config_path: str = "config.ini") -> str:
    """
    获取高德API密钥：优先从环境变量读取，其次从配置文件读取
    环境变量名：AMAP_API_KEY；配置文件section：[AMAP]，key：api_key
    """
    # 1. 优先读取环境变量（避免配置文件依赖）
    env_api_key = os.getenv("AMAP_API_KEY")
    if env_api_key and env_api_key.strip():
        return env_api_key.strip()

    # 2. 环境变量未设置，读取配置文件
    config = configparser.ConfigParser()
    if not config.read(config_path, encoding="utf-8"):
        raise FileNotFoundError(
            f"配置文件不存在：{config_path}\n"
            "解决方式：1. 创建config.ini（[AMAP] -> api_key=你的密钥）；2. 设置环境变量 AMAP_API_KEY=你的密钥"
        )

    try:
        api_key = config.get("AMAP", "api_key").strip()
        if not api_key:
            raise configparser.NoOptionError(
                option="api_key",
                section="AMAP",
                message="api_key为空，请在config.ini的[AMAP] section中填写有效密钥"
            )
        return api_key
    except configparser.NoSectionError:
        raise configparser.NoSectionError(
            section="AMAP",
            message="配置文件缺少[AMAP] section，请添加后重试"
        )
    except configparser.NoOptionError as e:
        raise e


# -------------------------- 3. 核心辅助函数（动态ADCODE获取 + API请求） --------------------------
async def get_adcode_by_city_name(city_name: str) -> Optional[str]:
    """
    动态获取城市ADCODE（基于高德地理编码API）
    支持任意中国城市，无需手动维护编码表
    """
    try:
        api_key = get_amap_api_key()
        # 高德地理编码API：限定城市名匹配，避免歧义（如"开封"只匹配河南省开封市）
        geo_url = "https://restapi.amap.com/v3/geocode/geo"
        params = {
            "key": api_key,
            "address": city_name,  # 城市名称（如"蚌埠"）
            "city": city_name,     # 限定查询范围，提高匹配准确性
            "output": "JSON"
        }

        print(f"[调试] 动态获取ADCODE：城市={city_name}，请求参数={params}", file=sys.stderr)
        async with httpx.AsyncClient() as client:
            response = await client.get(geo_url, params=params, timeout=15.0)
            response.raise_for_status()

            # 解析响应
            try:
                data = response.json()
            except json.JSONDecodeError as e:
                print(f"[错误] 动态获取ADCODE：API返回非JSON格式 - {e}", file=sys.stderr)
                return None

            print(f"[调试] 动态获取ADCODE响应：{data}", file=sys.stderr)
            # 高德API：status=1表示成功，geocodes不为空表示有匹配结果
            if data.get("status") != "1" or not data.get("geocodes"):
                info = data.get("info", "未知错误")
                print(f"[错误] 动态获取ADCODE失败：城市={city_name}，原因={info}", file=sys.stderr)
                return None

            # 提取ADCODE（取第一个匹配结果，通常是最准确的）
            return data["geocodes"][0]["adcode"]

    except Exception as e:
        print(f"[错误] 动态获取ADCODE异常：城市={city_name}，错误={str(e)}", file=sys.stderr)
        return None


async def make_amap_weather_request(
    city_adcode: str,
    extensions: str = "base"  # "base"=实时天气，"all"=未来3天预报（高德免费版上限）
) -> Optional[dict[str, Any]]:
    """
    封装高德天气API请求：支持实时天气和预报
    extensions参数说明：base=实时天气，all=未来3天预报
    """
    try:
        api_key = get_amap_api_key()
        weather_url = "https://restapi.amap.com/v3/weather/weatherInfo"
        params = {
            "key": api_key,
            "city": city_adcode,
            "extensions": extensions,
            "output": "JSON"
        }

        print(f"[调试] 高德天气API请求：ADCODE={city_adcode}，类型={extensions}", file=sys.stderr)
        print(f"[调试] 请求参数：{params}", file=sys.stderr)

        async with httpx.AsyncClient() as client:
            response = await client.get(
                weather_url,
                params=params,
                timeout=30.0,
                headers={"Content-Type": "application/json; charset=utf-8"}
            )
            response.raise_for_status()

            # 解析JSON响应
            try:
                data = response.json()
            except json.JSONDecodeError as e:
                print(f"[错误] 天气API返回非JSON格式 - {e}", file=sys.stderr)
                return None

            print(f"[调试] 天气API响应：{data}", file=sys.stderr)
            # 高德API：status=1表示请求成功
            if data.get("status") != "1":
                info = data.get("info", "未知错误")
                infocode = data.get("infocode", "未知错误码")
                print(f"[错误] 天气API请求失败：状态={data.get('status')}，信息={info}，错误码={infocode}", file=sys.stderr)
                return None

            return data

    # 已知异常处理（具体提示，帮助用户定位）
    except FileNotFoundError as e:
        print(f"[错误] 配置问题：{e}", file=sys.stderr)
        return None
    except (configparser.NoSectionError, configparser.NoOptionError) as e:
        print(f"[错误] 配置文件问题：{e}", file=sys.stderr)
        return None
    except TimeoutException:
        print(f"[错误] 天气API请求超时（超过30秒），请检查网络", file=sys.stderr)
        return None
    except HTTPError as e:
        status_code = e.response.status_code if e.response else "未知"
        print(f"[错误] HTTP请求失败：状态码={status_code}，原因={str(e)}", file=sys.stderr)
        return None
    except Exception as e:
        print(f"[错误] 天气API请求异常：{str(e)}", file=sys.stderr)
        return None


# -------------------------- 4. MCP工具函数（实时天气 + 预报） --------------------------
@mcp.tool()
async def get_current_weather(city: str) -> str:
    """
    获取国内任意城市的实时天气信息（基于高德地图API）
    
    Args:
        city: 城市名称（如"蚌埠"、"北京"、"上海"）
    """
    # 步骤1：动态获取城市ADCODE
    city_adcode = await get_adcode_by_city_name(city)
    if not city_adcode:
        return f"""
❌ 无法查询"{city}"的实时天气
可能原因：
1. 城市名称不准确（如"蚌阜"应为"蚌埠"）
2. 该地区暂未被高德API收录（极少发生）
建议：尝试查询其他城市（如"北京"、"上海"）验证服务是否正常
        """.strip()

    # 步骤2：调用高德API获取实时天气（extensions=base）
    weather_data = await make_amap_weather_request(city_adcode, extensions="base")
    if not weather_data:
        return f"""
❌ 无法获取"{city}"的实时天气数据
可能原因：
1. 高德API密钥无效或过期
2. 网络连接不稳定
建议：检查API密钥配置，或稍后重试
        """.strip()

    # 步骤3：解析并格式化结果
    lives = weather_data.get("lives", [])
    if not lives:
        return f"❌ 未获取到{city}的实时天气数据（API返回空结果）"

    live = lives[0]
    return f"""
✅ {city}实时天气信息
┌─────────────────────────────────────┐
│ 数据发布时间：{live.get("reporttime", "未知")} │
│ 省份：{live.get("province", "未知")}                │
│ 城市：{live.get("city", "未知")}                  │
│ 天气现象：{live.get("weather", "未知")}            │
│ 实时气温：{live.get("temperature", "未知")}℃       │
│ 风向：{live.get("winddirection", "未知")}          │
│ 风力级别：{live.get("windpower", "未知")}级        │
│ 空气湿度：{live.get("humidity", "未知")}%          │
└─────────────────────────────────────┘
    """.strip()


@mcp.tool()
async def get_forecast(city: str) -> str:
    """
    获取国内任意城市的未来3天天气预报（基于高德地图API，免费版上限）
    
    Args:
        city: 城市名称（如"蚌埠"、"北京"、"上海"）
    """
    # 步骤1：动态获取城市ADCODE
    city_adcode = await get_adcode_by_city_name(city)
    if not city_adcode:
        return f"""
❌ 无法查询"{city}"的天气预报
可能原因：
1. 城市名称不准确（如"蚌阜"应为"蚌埠"）
2. 该地区暂未被高德API收录（极少发生）
建议：尝试查询其他城市（如"北京"、"上海"）验证服务是否正常
        """.strip()

    # 步骤2：调用高德API获取预报（extensions=all，返回未来3天）
    forecast_data = await make_amap_weather_request(city_adcode, extensions="all")
    if not forecast_data:
        return f"""
❌ 无法获取"{city}"的天气预报数据
可能原因：
1. 高德API密钥无效或过期
2. 网络连接不稳定
建议：检查API密钥配置，或稍后重试
        """.strip()

    # 步骤3：解析并格式化结果
    forecasts = forecast_data.get("forecasts", [])
    if not forecasts:
        return f"❌ 未获取到{city}的预报数据（API返回空结果）"

    forecast = forecasts[0]
    casts = forecast.get("casts", [])
    if not casts:
        return f"❌ 未获取到{city}的详细预报数据"

    # 格式化未来3天预报（高德免费版最多返回3天）
    day_labels = ["今天", "明天", "后天"]
    forecast_text = []
    for i, cast in enumerate(casts[:3]):  # 取前3天（避免超出免费版限制）
        day_label = day_labels[i] if i < len(day_labels) else f"第{i+1}天"
        forecast_text.append(f"""
{day_label}（{cast.get("date", "未知日期")} | 星期{cast.get("week", "未知")}）
├─ 白天：{cast.get("dayweather", "未知")} | {cast.get("daytemp", "未知")}℃
│  风向：{cast.get("daywind", "未知")} | 风力：{cast.get("daypower", "未知")}级
└─ 晚上：{cast.get("nightweather", "未知")} | {cast.get("nighttemp", "未知")}℃
   风向：{cast.get("nightwind", "未知")} | 风力：{cast.get("nightpower", "未知")}级
""")

    return f"""
✅ {city}未来3天天气预报
┌─────────────────────────────────────┐
│ 预报发布时间：{forecast.get("reporttime", "未知")} │
│ 省份：{forecast.get("province", "未知")}                │
│ 城市：{forecast.get("city", "未知")}                  │
└─────────────────────────────────────┘
{''.join(forecast_text).strip()}
    """.strip()


# -------------------------- 5. 启动服务器 --------------------------
if __name__ == "__main__":
    try:
        # 启动前验证API密钥（提前暴露配置问题，避免客户端调用时报错）
        get_amap_api_key()
        print(f"[调试] MCP天气服务器启动成功（基于高德API），等待客户端连接...", file=sys.stderr)
        mcp.run(transport="stdio")
    except Exception as e:
        print(f"[致命错误] 服务器启动失败：{str(e)}", file=sys.stderr)
        sys.exit(1)